Shell脚本执行需要 root 权限的命令
直接上代码,命令如下:
echo "passwd"|sudo -S command
将passwd
替换为你的root
权限需要的密码,command
替换为需要执行的命令即可。
example:
比如 test.sh 脚本内容如下
#!/bin/bash
echo "123456"|sudo -S cp -rf hosts.txt /etc/hosts
加载过慢请开启缓存(浏览器默认开启)
直接上代码,命令如下:
echo "passwd"|sudo -S command
将passwd
替换为你的root
权限需要的密码,command
替换为需要执行的命令即可。
example:
比如 test.sh 脚本内容如下
#!/bin/bash
echo "123456"|sudo -S cp -rf hosts.txt /etc/hosts
该项目基于 Github
项目整合的远程 Hosts
直链,适配多种规则、终端,每30分钟自动同步一次Github
最新可用项目并提供打包下载。所以我们可以替换本地的hosts
文件,已达到访问Google,Facebook,Instagram
等网站的目的。
这里,我们只需要周期执行脚本,更新我们的hosts
文件就可以了。
首先为们clone
一下工程
git clone https://github.com/forkgood/easyhosts.git
然后将我们的更新脚本放到clone
的目录中。
比如更新hosts
文件的脚本叫schedule-host.sh
,其内容如下:
#!/bin/bash
git pull
# 将 pwd 替换为你电脑的登陆密码,也就是 root 权限需要的密码。
# mac 中需要使用 hosts.txt 文件
echo "pwd"|sudo -S cp -rf hosts.txt /etc/hosts
接下来就是周期执行脚本了。
主要有两种方式:
本次主要讲一下launchctl
的使用。
plist
文件比如文件名为im.wangchao.schedulehosts.plist
,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>im.wangchao.schedulehosts</string> <!-- 名称 -->
<key>ProgramArguments</key>
<array>
<string>/Users/xxx/xxx/easyhosts/schedule-host.sh</string> <!-- 需要执行的脚本,绝对路径,这里直接使用上面创建的更新 hosts 的脚本 -->
</array>
<key>StartInterval</key>
<integer>18000</integer> <!-- 执行的周期间隔,单位为秒 -->
</dict>
</plist>
为了确保我们的plist
文件正确,我们可以使用plutil -lint
对文件进行校验。
plist
文件将我们刚刚创建的plist
文件保存到/Library/LaunchAgents
目录下即可。
其中/System/Library
,/Library
和~/Library
目录的区别?
/System/Library
目录是存放Apple自己开发的软件/Library
目录是系统管理员存放的第三方软件~/Library/
是用户自己存放的第三方软件LaunchDaemons
和LaunchAgents
的区别?
LaunchDaemons
是用户未登陆前就启动的服务(守护进程)LaunchAgents
是用户登陆后启动的服务(守护进程)plist
文件相关命令如下:
# 加载任务
launchctl load -w im.wangchao.schedulehosts.plist
# 卸载任务
launchctl unload -w im.wangchao.schedulehosts.plist
# 查看任务列表
launchctl list | grep 'im.wangchao.schedulehosts'
这几天把自己的博客配置了Https证书,这里主要记录一下我的配置过程。
首先采用的是Let’s Encrypt颁发的免费证书,其次我是使用acme.sh配置的,这里主要说一下acme.sh的安装以及使用。
官方方法使用如下命令即可:
curl https://get.acme.sh | sh
如果执行失败,可以下载master分之最新代码,解压之后进入目录执行如下命令:
./acme.sh --install
安装成功后,acme.sh脚本在~/.acme.sh目录中,所以如果想直接执行脚本,那么需要设置环境变量,后文中因为配置过了环境变量所以直接执行了acme.sh脚本。
安装成功后,我们需要创建一个存放Let’s Encrypt验证文件的文件夹(最好不要存放到root目录中),比如我们创建如下目录:
mkdir -p /www/acme-challenges
最近把App的所有请求都换成Https,在测试的时候,部分手机发现请求失败,失败的异常信息如下:
javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x783e8e70: Failure in SSL library, usually a protocol error
error:14077102:SSL routines:SSL23_GET_SERVER_HELLO:unsupported protocol (external/openssl/ssl/s23_clnt.c:714 0x71a20cf8:0x00000000)
该异常为握手失败,但是为什么有的手机可以成功有的手机又失败了呢,首先查看我们服务端接口TLS支持的版本为1.x,后来发现失败的手机都是5.x以下的版本,推测应该是和这个有关,然后查阅官方文档,SSLSocket中有提到TLS版本和Android SDK版本的对应表,如下:
Protocol | Supported (API Levels) | Enabled by default (API Levels) |
---|---|---|
SSLv3 | 1+ | 1+ |
TLSv1 | 1+ | 1+ |
TLSv1.1 | 16+ | 20+ |
TLSv1.2 | 16+ | 20+ |
通过这个表看到,TLSv1.x(1.1,1.2)Android默认从API16开始支持,而从API20开始默认可用,这就可以解释之前为什么5.x以下手机在进行请求时失败了。
Android 7.0 也出一阵子了,在这里分享一些关于 Android 7.0 适配的心得体会。
首先你要知道 Android 7.0 强制执行了StrictMode API 政策
,目录被限制了访问。所以在我们日常开发中建议开启StrictMode
。
在 Android 7.0 手机上,当我们调用相机照相,或者传递 file://
Uri 到其它 App 时,我们会遇到FileUriExposedException
异常,这是因为强制执行StrictMode
后,禁止向其它 App 公开 file://
Uri,也就是说当一个包含 file://
Uri 的的 Intent
离开当前 App 时候就会出现FileUriExposedException
异常。
在 Android 7.0 上我们对应的解决方案就是使用FileProvider
,获取一个content://
Uri 来完成我们的操作(当然低于7.0的版本,我们还用原来的方法即可)。
首先创建自己的 FileProvider
,为什么要创建自己的FileProvider
,当我们的工程引入第三方库时,难免会引入已经注册android.support.v4.content.FileProvider
的库,那么我们AndroidManifest.xml
就是合并失败,为了避免我们可以定义自己的FileProvider
,如下:
package com.xxx.test.TestFileProvider;
...
public class TestProvider extends FileProvider {
public static final String AUTHORITY = "com.xxx.test.fileprovider";
public static Uri compatUriFromFile(Context context, File file){
return compatUriFromFile(context, file, null);
}
public static Uri compatUriFromFile(Context context, File file, Intent intent) {
if (!needUseProvider()){
return Uri.fromFile(file);
}
if (intent != null){
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
return getUriForFile(context, AUTHORITY, file);
}
public static boolean needUseProvider(){
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
}
}
然后在AndroidManifest.xml
中注册我们的FileProvider
,如下:
<provider
android:name="com.xxx.test.TestProvider"
android:authorities="com.xxx.test.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"/>
</provider>
注意exported:要求必须为false,若为true则会报安全异常
,grantUriPermissions:true,表示授予 URI 临时访问权限
。
接下来在res
目录中创建xml
目录,并在里面创建file_paths.xml
文件。该文件中指定可以访问目录,如下:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="my_images" path="Android/data/com.xxx.test/files/Pictures" />
</paths>
我们主要看里面的节点,可以为files-path
,cache-path
,external-path
,external-files-path
和external-cache-path
。
files-path
代表的根目录为:Context.getFilesDir()
cache-path
代表的根目录为:Context.getCacheDir()
external-path
代表的根目录为:Environment.getExternalStorageDirectory()
external-files-path
代表的根目录为:Context#getExternalFilesDir(String)
external-cache-path
代表的根目录为:Context.getExternalCacheDir()
节点的path
属性代表相对目录,如果该属性设置为""
,那么代表可以访问根目录的所有文件,如果像上面那样配置,那么则可以访问/storage/emulated/0/Android/data/com.xxx.test/files/Pictures
目录。
配置完之后,我们就可以使用我们创建的FileProvider
了,只需要将之前的Uri.fromFile()
替换成TestProvider.compatUriFromFile()
即可,比如调起照相机:
Intent photoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File file = null;
try {
// 创建文件
file = createImageFile();
} catch (IOException e) {
e.printStackTrace();
}
if (file != null){
photoIntent.putExtra(MediaStore.EXTRA_OUTPUT, TestProvider.compatUriFromFile(this, file, photoIntent));
startActivityForResult(photoIntent, CAPTURE_CUSTOM_FILE_REQUEST_CODE);
}
在低电耗模式下,当用户设备未插接电源、处于静止状态且屏幕关闭时,该模式会推迟 CPU 和网络活动,从而延长电池寿命。
Android7.0通过在设备未插接电源且屏幕关闭状态下、但不一定要处于静止状态(例如用户外出时把手持式设备装在口袋里)时应用部分 CPU 和网络限制,进一步增强了低电耗模式。(也就是说,Android7.0会在手机屏幕关闭的状态下,限时应用对CPU以及网络的使用。)
具体规则如下:
官方文档
Android 7.0中删除了三项隐式广播,以帮助优化内存使用和电量消耗。
CONNECTIVITY_ACTION
广播,即使你在manifest清单文件中设置了请求接受这些事件的通知。 但在前台运行的应用如果使用BroadcastReceiver请求接收通知,则仍可以在主线程中侦听CONNECTIVITY_CHANGE
;ACTION_NEW_PICTURE
或 ACTION_NEW_VIDEO
类型的广播。Android 框架提供多个解决方案来缓解对这些隐式广播的需求。 例如,JobScheduler API
提供了一个稳健可靠的机制来安排满足指定条件(例如连入无限流量网络)时所执行的网络操作。 您甚至可以使用 JobScheduler API
来适应内容提供程序变化。
移动设备会经历频繁的连接变更,例如在 Wi-Fi 和移动数据之间切换时。 目前,可以通过在应用清单中注册一个接收器来侦听隐式 CONNECTIVITY_ACTION
广播,让应用能够监控这些变更。 由于很多应用会注册接收此广播,因此单次网络切换即会导致所有应用被唤醒并同时处理此广播。
Android开发中,Activity的使用是不可或缺的,在使用Activity的过程中通常会给特殊的Activity设置不同的启动模式,而这些启动模式也影响着Activity所在的任务栈,今天就先从Activity的启动模式说起,然后总结一些关于影响Activity任务栈的那些事。
Activity标准启动模式,也是默认启动模式。在这种模式下启动的Activity可以被多次实例化,也就是说在同一个任务栈中可以存在多个Activity的实例,每个实例都会处理一个Intent对象。
比如,我们有一个MainActivity,MainActivity的代码如下
public class MainActivity extends AppCompatActivity {
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.mTestBtn).setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, MainActivity.class);
//intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
});
setTitle(getClass().getSimpleName());
Log.e("wcwcwc", getClass().getSimpleName() + " onCreate Task ID : " + getTaskId());
}
}
执行点击按钮跳转MainActivity,此时控制台的日志如下:
11-09 14:54:16.528 21613-21613/im.wangchao.launchemode E/wcwcwc: MainActivity onCreate Task ID : 5634
11-09 14:54:18.198 21613-21613/im.wangchao.launchemode E/wcwcwc: MainActivity onCreate Task ID : 5634
可以看出创建的多个MainActivity的实例,并且它们是在同一个任务栈中。
首先,什么是代理,拿出你的手机,打开微信朋友圈,看看朋友圈的微商,这就是代理。代理就是一个对象代表代表另一个对象做其需要做的事,就像刚才说的微商,你的朋友就是代理了原厂商。现在大概知道了代理是什么,那么Android中有哪些代理呢,举个最简单的例子,我们都知道__Context__,__Context__就是使用了代理模式,__ContextImpl__实现了__Context__的所有功能,ContextWrapper__即为代理类,也实现了__Context__类,里面包含的__Context__引用为__ContextImpl,所以__ContextWrapper__调用的方法都是__ContextImpl__中的实现,这就是典型的代理模式。
上一篇文章关于__Bolts Task__的使用做了简单的说明,这次我们注意解析一下__Bolts Task__的源码,来看看它具体是怎么实现的。